#include "util.h"
#include <stdio.h>
#include <mmsystem.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
namespace util
{
	CSystem* g_p_system = NULL;
	CSystem::CSystem(unsigned int w, unsigned int h, const char* t)
	{
		m_hwnd = NULL;
		m_hdc = NULL;
		m_paused = false;
		m_p_init_func = m_p_frame_func = m_p_render_func = NULL;
		m_fixed_delta = 1000 / 60;
		m_win_width = w;
        m_win_height = h;
		m_fps = 0;
		
        create_window(t);
		
		if (!(m_hrc = wglCreateContext(m_hdc)))
			PostQuitMessage(0);
		if (!wglMakeCurrent(m_hdc, m_hrc))
			PostQuitMessage(0);
	}
	CSystem::~CSystem()
	{
		if (!wglMakeCurrent(NULL, NULL))
			MessageBoxA(NULL, "OpenGL wglMakeCurrent() fail...", "error", 0);
		if (!wglDeleteContext(m_hrc))
			MessageBoxA(NULL, "OpenGL wglDeleteContext() fail...", "error", 0);
			
		ReleaseDC(m_hwnd, m_hdc);
	}
	void CSystem::set_callback(Func option, callback p_func)
	{
		switch (option)
		{
		case INIT:
			m_p_init_func = p_func;
			break;
		case FRAME:
			m_p_frame_func = p_func;
			break;
		case RENDER:
			m_p_render_func = p_func;
			break;
		default:
		    break;
		}
	}
	void CSystem::run()
	{
	    //create_window();
		if (m_p_init_func != NULL)
			m_p_init_func();

		// main loop
		MSG msg;
		unsigned long last = timeGetTime();
		while (true)
		{
			if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
			{
			    if (msg.message == WM_QUIT)
                    break;
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
			else
			{
			    if (!m_paused)
                {
                    unsigned int dt = timeGetTime() - last;
                    if (dt > m_fixed_delta)
                    {
                        last = timeGetTime();
						
                        if (m_p_frame_func != NULL)
                            m_p_frame_func();	
                        if (m_p_render_func != NULL)
                            m_p_render_func();
						glerr();
						SwapBuffers(m_hdc);
                    }
                    else
                    {
                        if (dt + 5 < m_fixed_delta)
                            Sleep(1);
                    }
                }
                else
                {
                    Sleep(1);
                }
			}
		}
	}
	LRESULT CSystem::msg_proc(UINT msg, WPARAM wparam, LPARAM lparam)
	{
		switch (msg)
		{
		case WM_ACTIVATE:
			if (LOWORD(wparam) == WA_INACTIVE)
				m_paused = true;
			else
				m_paused = false;
			return 0;
		case WM_SIZE:
			//g_p_openGL->resize(LOWORD(lparam), HIWORD(lparam));
			return 0;
		case WM_CLOSE:
			DestroyWindow(m_hwnd);
			return 0;
		case WM_DESTROY:
			PostQuitMessage(0);
			return 0;
		}
		return DefWindowProc(m_hwnd, msg, wparam, lparam);
	}
	unsigned long CSystem::tick(void) const   { return timeGetTime(); }
	int CSystem::fps(void) const              { return m_fps; }
	void CSystem::create_window(const char* title)
	{
	    HINSTANCE hinstance = GetModuleHandleA(NULL);
		WNDCLASSA wc;
		wc.style = CS_HREDRAW | CS_VREDRAW;
		wc.lpfnWndProc = WndProc;
		wc.cbClsExtra = 0;
		wc.cbWndExtra = 0;
		wc.hInstance = hinstance;
		wc.hIcon = LoadIcon(0, IDI_APPLICATION);
		wc.hCursor = LoadCursor(0, IDC_ARROW);
		wc.hbrBackground = 0;
		wc.lpszMenuName = 0;
		wc.lpszClassName = "yqq";
		if (!RegisterClassA(&wc))
			syserr();
		int width = m_win_width + GetSystemMetrics(SM_CXFIXEDFRAME) * 2;
		int height = m_win_height + GetSystemMetrics(SM_CYFIXEDFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION);
		int xpos = (GetSystemMetrics(SM_CXSCREEN) - width) / 2;
		int ypos = (GetSystemMetrics(SM_CYSCREEN) - height) / 2;
		m_hwnd = CreateWindowA("yqq", title, WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX, xpos, ypos, width, height, NULL, NULL, hinstance, NULL);
		if (!m_hwnd)
            syserr();
		PIXELFORMATDESCRIPTOR pfd =
		{
			sizeof(PIXELFORMATDESCRIPTOR),
			1,
			PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
			PFD_TYPE_RGBA,
			32,
			0, 0, 0, 0, 0, 0,
			0,
			0,
			0,
			0, 0, 0, 0,
			16,
			1,   // enable stencil buffer
			0,
			PFD_MAIN_PLANE,
			0,
			0, 0, 0
		};
		unsigned int pixel_format = 0;
		if (!(m_hdc = GetDC(m_hwnd)))
			syserr();
		if (!(pixel_format = ChoosePixelFormat(m_hdc, &pfd)))
			syserr();
		if (!SetPixelFormat(m_hdc, pixel_format, &pfd))
			syserr();
		ShowWindow(m_hwnd, SW_SHOW);
		SetForegroundWindow(m_hwnd);
		UpdateWindow(m_hwnd);
	}
	void CSystem::syserr(void) const
	{
        unsigned int e = GetLastError();
        printf("CSystem error %d\n", e);
        PostQuitMessage(0);
	}
	void CSystem::glerr(void) const
	{
		GLenum e = glGetError();
		if (e == GL_NO_ERROR)   return;
		printf("glGetError:%d...\n", e);
	}
}

//////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	if (util::g_p_system != NULL)
		return util::g_p_system->msg_proc(msg, wparam, lparam);
	else
		return DefWindowProc(hwnd, msg, wparam, lparam);
}
